from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseForbidden, JsonResponse
from django import http
import re, json, logging
from django.urls import reverse
from django.db import DatabaseError
from django.contrib.auth import login, authenticate, logout
from django_redis import get_redis_connection
from django.contrib.auth.mixins import LoginRequiredMixin

from django.views import View

from users import constants
from users.models import User
from meiduo_01.utils.response_code import RETCODE
from meiduo_01.utils.views import LoginRequiredJsonMixin
from celery_tasks.email.tasks import send_verify_mail
from users.utils import generate_verify_email_url, check_verify_email_token
from users.models import Address
from carts.utils import merge_carts_cookies_redis
# Create your views here.

logger = logging.getLogger('django')


class UpdateTitleAddressView(LoginRequiredJsonMixin, View):
    """修改地址标题"""
    def put(self, req, address_id):
        # 接收参数
        json_dict = json.loads(req.body.decode())
        title = json_dict.get('title')

        # 校验参数
        if not title:
            return http.HttpResponseForbidden('缺少title')
        try:
            # 查询当前要更新标题的地址
            # address = Address.objects.get(title=title)
            address = Address.objects.get(id=address_id)

            # 新标题覆盖旧标题
            address.title = title
            address.save()
        except Exception as e:
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '修改标题失败'})
        # 响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '修改标题cg'})


        pass


class DefaultAddressView(LoginRequiredJsonMixin, View):
    """设置默认地址"""
    def put(self, req, address_id):

        try:
            # 查询出当前要把哪个地址作为默认地址
            address = Address.objects.get(id=address_id)

            # 将指定地址设置成 当前用户的默认地址
            req.user.default_address = address
            req.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})


class UpdateDestroyAddressView(LoginRequiredJsonMixin, View):
    """更新和删除地址"""
    def put(self, req, address_id):
        """
        更新 修改地址
        :param req:
        :return:
        """
        # 接收参数
        json_dict = json.loads(req.body.decode())

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 使用最新的地址信息覆盖【指定的】旧信息
        try:
            Address.objects.get(id=address_id).update(
                user=req.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email,
            )
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '修改地址失败'})

        # 响应新的地址信息给前端
        address = Address.objects.get(id=address_id)
        address_dict = {
            'id': address.id,
            'receiver': address.receiver,
            'title': address.title,
            'province': address.province.name,  # 这是从数据库中取数据
            'city': address.city.name,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email
        }

        return JsonResponse({'code': RETCODE.OK, 'errmsg': '修改地址成功', 'address': address_dict})

    def delete(self, req, address_id):
        """删除收货地址"""
        # 实现指定地址的逻辑删除
        # is_deleted = True
        try:
            address = Address.objects.get(id=address_id)
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})


class AddressCreateView(LoginRequiredJsonMixin, View):
    """新增地址"""
    def post(self, req):
        """
        实现新增地址逻辑
        :param req:
        :return:
        """
        # 判断用户地址数量是否超出上限：如果超出，最早结束请求，连参数都不接收
        count = req.user.addresses.count()
        if count > constants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超出用户地址上线'})

        # 接收参数
        json_dict = json.loads(req.body.decode())

        receiver = json_dict.get('receiver')

        # province = json_dict.get('province')
        # city =json_dict.get('city')
        # district = json_dict.get('district')

        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')

        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 保存用户传入的地址信息到数据库
        try:
            # TODO 不熟
            # 在Address模型类中添加数据，即 在tb_areas 中添加数据
            address = Address.objects.create(
                # user=req.user, 注意这一步
                user=req.user,
                title=receiver,  # 标题默认就是收货人
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            # 如果登录用户没有默认的地址，我们需要指定默认的地址
            if not req.user.default_address:
                req.user.default_address = address
                req.user.save()  # 老师一直嘱咐的，还是忘了

        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})

        # 构造新增地址字典数据,响应给前端 实现局部刷新
        address_dict = {
            'id': address.id,
            'receiver': address.receiver,
            'title': address.title,
            'province': address.province.name,  # 这是从数据库中取数据
            'city': address.city.name,
            'district': address.district.name,
            'place': address.place,
            'mobile': address.mobile,
            'tel': address.tel,
            'email': address.email
        }

        # 响应新增地址的结果：需要将新增的地址返回给前端渲染
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})

        pass


class AddressView(LoginRequiredMixin, View):
    """展示地址"""
    def get(self, req):
        """【查询】并展示所有收货地址"""
        # 获取当前登录用户对象
        login_user = req.user

        # 使用当前登录用户和is_deleted作为条件 筛选地址数据
        # addresses = login_user.objects.filter(user=login_user, is_deleted=False)
        addresses = Address.objects.filter(user=login_user, is_deleted=False)

        # 将用户地址模型列表转字典列表，因为
        # JsonResponse 和 Vue.js不认识模型类型
        # 只有django和jinja2模板引擎认识
        address_list = []
        for address in addresses:
            address_dict = {
                'id': address.id,
                'receiver': address.receiver,
                'title': address.title,
                'province': address.province.name,  # 这是从数据库中取数据
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }
            address_list.append(address_dict)

        # 构造上下文
        context = {
            'default_address_id': login_user.default_address_id or '',
            'addresses': address_list
        }

        return render(req, 'user_center_site.html', context)


class VerifyEmailView(View):
    """验证邮箱"""
    def get(self, req):

        # 接收参数
        token = req.GET.get('token')
        # 校验参数
        if token is None:
            return HttpResponseForbidden('缺少token')
        # 从token中提取用户信息 user_id  ===> user
        # 封装函数
        user = check_verify_email_token(token)
        if user is None:
            return HttpResponseForbidden('无效的token')

        # 将email_active字段设置成True
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('激活邮箱失败')

        # 响应结果 重定向到用户中心
        return redirect(reverse('users:info'))


class EmailView(LoginRequiredJsonMixin, View):
    """添加邮箱，发送邮件 邮箱验证链接"""
    def put(self,req):
        """
        :param req:
        :return:
        """
        # 接收参数
        json_str = req.body
        json_dict = json.loads(json_str)
        email = json_dict.get('email')

        # 校验
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}', email):
            return HttpResponseForbidden('参数email有误')

        # 最主要：将用户传入的邮箱，保存到用户数据库的email字段中（取当前登录用户的邮箱，所以用这种方式来取）
        # req.user.email = 'email'
        try:
            req.user.email = email
            req.user.save()
        except Exception as e:
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})

        # 发送邮箱验证邮件

        # send_verify_mail(email,verify_url='http://www.baidu.com')
        verify_url = generate_verify_email_url(req.user)
        send_verify_mail.delay(email, verify_url)

        # 响应结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK_email'})


class UserInfoView(LoginRequiredMixin, View):
    """用户中心"""

    def get(self, req):
        """
        提供用户中心页面
        :param req:
        :return:
        """

        # 判断用户是否登录，登录了返回True，反之False
        # if req.user.is_authenticated:
        #     return render(req, 'user_center_info.html', )
        # else:
        #     return render(req, 'login.html')
        context = {
            'username': req.user.username,
            'mobile': req.user.mobile,
            'email': req.user.email,
            'email_active': req.user.email_active,
        }
        return render(req, 'user_center_info.html', context=context)


class LogoutView(View):
    """退出登录"""
    def get(self, req):
        """实现用户退出登录的逻辑"""
        # 清除状态保持的信息
        logout(req)

        # 响应结果
        # return redirect(reverse('contents:index'))

        response = redirect(reverse('contents:index'))
        response.delete_cookie('username')

        return response


class LoginView(View):
    """用户登录"""
    def get(self, req):
        """
        提供用户登陆页面
        :param req:
        :return: html
        """
        return render(req, 'login.html')

    def post(self, req):
        """
        实现用户登陆逻辑
        :param req:
        :return: html
        """

        # 接收参数
        username = req.POST.get('username')
        password = req.POST.get('password')
        remembered = req.POST.get('remembered')

        # 校验参数
        if not all([username, password]):
            return HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return HttpResponseForbidden('请输入5-20个字符的用户名')
        # 密码  -- 符合规则
        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位密码')

        # 认证用户：使用账号查询用户是否存在，再校验密码是否正确
        # user = User.objects.get(username=username)
        # user.check_password()
        user = authenticate(username=username, password=password)
        if not user:
            # return 'html'
            return render(req, 'login.html', {'account_errmsg': '账号或密码错误'})

        # 状态保持
        login(req, user)

        if remembered != 'on':
            # 如果不记住登陆，状态保持在浏览器关闭时销毁
            req.session.set_expiry(0)  # 单位是秒
        else:
            # None  表示默认的两周
            # req.session.set_expiry(3600 * 24 * 14)
            req.session.set_expiry(None)

        # 响应登录的结果
        # 响应结果
        # 先取出next
        next = req.GET.get('next')
        if next:
            # 重定向到next
            response = redirect(next)
        else:
            # 重定向到首页
            response = redirect(reverse('contents:index'))

        # 在响应中加入cookie
        response.set_cookie('username', user.username, max_age=3600 * 24 * 14)

        # 返回响应:重定向到首页
        # return redirect(reverse('contents:index'))

        # 用户登录成功，合并购物车(参数多，使用关键字参数比较安全)
        response = merge_carts_cookies_redis(req=req, user=user, response=response)

        return response


class MobileCountView(View):
    """判断手机号是否重复注册"""
    def get(self,req, mobile):
        """
        :param req:
        :param mobile:
        :return: JSON
        """
        count = User.objects.filter(mobile=mobile).count()
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '成功', 'count': count})


class UsernameCountView(View):
    """判断用户名是否重复注册"""
    def get(self,req, username):
        """
        :param req:
        :param username: 传过来的用户名
        :return: JSON
        """
        count = User.objects.filter(username=username).count()
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '成功', 'count': count})


class RegisterView(View):
    """用户注册"""

    def get(self, req):
        """提供用户注册页面"""
        reverse('users:register')
        return render(req, 'register.html')

    def post(self,req):
        """实现用户注册业务逻辑"""
        # 接收参数
        # v= req.POST.get('k')
        username = req.POST.get('username')
        password = req.POST.get('password')
        password2 = req.POST.get('password2')
        mobile = req.POST.get('mobile')
        sms_code_client = req.POST.get('sms_code')
        allow = req.POST.get('allow')

        # 校验参数
        # 参数齐全
        if not all([username, password, password2, mobile, allow]):
            return HttpResponseForbidden('非法请求，缺少必传参数001')
        # 用户名 --- 符合规则
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return HttpResponseForbidden('请输入5-20个字符的用户名')
        # 密码  -- 符合规则
        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位密码')
        # 重复密码  -- 和密码一样
        if password2 != password:
            return HttpResponseForbidden('两次密码不一致')
        # 手机  --符合规则
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return  HttpResponseForbidden('请输入正确的手机号码')
        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)
        if not sms_code_server:
            # return render(req, 'register.html', {'code':RETCODE.SMSCODERR, 'errmsg':'短信验证码已失效'})
            # TODO 渲染到哪儿去了？？
            return render(req, 'register.html', { 'sms_code_errmsg': '短信验证码已失效'})
        if sms_code_server.decode() != sms_code_client:
            return render(req, 'register.html', { 'sms_code_errmsg': '短信验证码有误'})

        # 协议 -- 勾选
        if allow != 'on':
            return HttpResponseForbidden('请勾选用户协议。。。。')
        # 只测试一小块代码，就提前，省去后面不必要的步骤
        # return render(req, 'register.html', {'register_errmsg': '注册失败'})

        # 将用户存到数据库（注册业务的核心）
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError:
            # return '告诉用户 注册失败（别说是数据库的原因）'
            return render(req, 'register.html', {'register_errmsg': '注册失败'})

        # 实现状态保持
        login(req, user)

        # 返回响应
        # 响应注册的结果
        response = redirect(reverse('contents:index'))

        # 在响应中加入cookie
        response.set_cookie('username', user.username, max_age=3600 * 24 * 14)

        # return HttpResponse('注册成功，重定向到首页')
        # return redirect('/')
        # return HttpResponse(reverse('contents:index'))   要用重定向
        return response
